# Author: ZhouYing
# CreatTime: 2022/10/22
# FileName: cell_segmentation_by_sub
# Description: Simple introduction of the code
from xml.etree import ElementTree as ET
import numpy as np
import cv2

from image_processing import *
from divide_assessment import *
from results_filter import *

xml_path_list = ["./train-5/184/184.xml", "./train-5/1308/1308.xml", "./train-5/1310/1310.xml",
                 "./train-5/1312/1312.xml", "./train-5/1315/1315.xml"]
image_path_list = ["./train-5/184/184.jpg", "./train-5/1308/1308.jpg", "./train-5/1310/1310.jpg",
                   "./train-5/1312/1312.jpg", "./train-5/1315/1315.jpg"]


def cell_segmentation_scanner_method(file_path_image):
    # 设置卷积核
    kernel = np.ones((3, 3), dtype=np.uint8)
    # 读取灰度图像
    image = gray_image = cv2.imread(file_path_image, 0)
    image_show(image, "0")
    # 将灰度图进行膨胀，以尽可能地使图上空白处的灰度提升在二值化时消除掉
    image = cv2.dilate(image, kernel, iterations=9)  # 调整膨胀参数不能超过10###
    image_show(image, "1")
    # 将膨胀后的图像二值化
    ret, image = cv2.threshold(image, 226, 255, cv2.THRESH_BINARY)
    image_show(image, "2")
    # 图像模糊，使显现出来的单独、没有细胞质的杂质像素值升高，即变明亮
    image = cv2.blur(image, (26, 26))
    image_show(image, "3")
    # 将原灰度图gray与模糊处理后的图像find进行像素值相加(add),由于模糊处理后的杂质
    # 的像素值会变得很高，即很亮，故相加后它在图像中会接近于“消失”！，以此来消除杂质
    image = cv2.add(image, gray_image)
    image_show(image, "4")
    # 图像二值化反转，让背景变黑，待圈出区域变白
    ret, image = cv2.threshold(image, 94, 255, cv2.THRESH_BINARY_INV)
    image_show(image, '5')
    # 寻找轮廓
    contours, hierarchy = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

    test_object = []
    for item in contours:
        # epsilon是拟合精度，表示的是原始曲线和近似曲线之间的最大距离
        # epsilon代表在0.001下的精度，数值越小，在一定程度上框出的轮廓越精确
        epsilon = 0.001 * cv2.arcLength(item, True)
        # approx记录着具体轮廓信息，我们可以通过它判断轮廓是几边形
        approx = cv2.approxPolyDP(item, epsilon, True)
        # 因为我们需要圈出的细胞核的形状是趋近于椭圆的，它的边数是接近于无穷大，我们只需要给定一个边数下限制来筛出其他边数不符合的轮廓，当然这并不能排除我们会误圈出杂质
        if len(approx) > 24:  # 30
            x, y, w, h = cv2.boundingRect(item)
            test_object.append({'regionX': x, 'regionY': y, 'regionWidth': w, 'regionHeight': h})

    # 删除重复出现的矩形框
    test_object = inspect(test_object)
    image_two = cv2.imread(file_path_image)
    for item in test_object:
        # 在图像上绘制矩形
        image_two = cv2.rectangle(image_two, (item["regionX"], item["regionY"]),
                                  (item["regionX"] + item['regionWidth'], item["regionY"] + item["regionHeight"]),
                                  (0, 0, 255), 3)
    image_show(image_two, "7")
    return test_object


# 测试方法在多个图像上的性能
def test_scanner_method_performance(xml_path_list, image_path_list):
    test_number = len(xml_path_list)
    for i in range(test_number):
        test_object = cell_segmentation_scanner_method(image_path_list[i])
        print(contrast(xml_path_list[i], test_object))


def test_cell_segmentation_scanner_method(file_path_image, image_dilate, first_threshold, blur_size, second_threshold,
                                           approx_size):
    kernel = np.ones(shape=(3, 3), dtype=np.uint8)
    image = gray_image = cv2.imread(file_path_image, 0)
    image = cv2.dilate(image, kernel, iterations=image_dilate)
    ret, image = cv2.threshold(image, first_threshold, 255, cv2.THRESH_BINARY)
    image = cv2.blur(image, (blur_size, blur_size))
    image = cv2.add(image, gray_image)
    ret, image = cv2.threshold(image, second_threshold, 255, cv2.THRESH_BINARY_INV)
    contours, hierarchy = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

    test_object = []
    for item in contours:
        epsilon = 0.001 * cv2.arcLength(item, True)
        approx = cv2.approxPolyDP(item, epsilon, True)
        if len(approx) > approx_size:
            x, y, w, h = cv2.boundingRect(item)
            test_object.append({'regionX': x, 'regionY': y, 'regionWidth': w, 'regionHeight': h})
    # 消除出现重叠框中的较大者
    test_object = inspect(test_object)
    return test_object


def series_parametric_tests_by_scanner(xml_path_list, image_path_list):
    file_number = len(image_path_list)
    all_xml_infor = get_all_xml_information(xml_path_list)
    with open("scanner_data.txt", 'a', encoding='utf-8') as f:
        for i in range(5, 10, 1):
            print(i)
            for j in range(220, 240, 2):
                for k in range(10, 28, 2):
                    for m in range(70, 130, 4):
                        for n in range(22, 28, 1):
                            # 查看同一参数的不同图像效果
                            part_list = []
                            for index in range(file_number):
                                test_object = test_cell_segmentation_scanner_method(image_path_list[index], i, j, k, m, n)
                                # print(test_object)
                                part_result = contrast_with_xml(all_xml_infor[index], test_object)
                                if part_result[0] < 0.27 or part_result[1] < 0.27:
                                    # print(part_result)
                                    break
                                else:
                                    part_list.append(part_result)
                            if len(part_list) == file_number:
                                f.write(str({"参数": [i, j, k, m, n], "性能": part_list})+"\n")
                                f.flush()
                                print("success")



''' 
对上面的主方法思路过程梳理总结：
1.图像灰度化，得到图像find，gray
2.find图像膨胀突出杂志
3.find图像二值化
4.find图像模糊化使杂质的像素值变高，即变得更亮
5.find与gray进行像素值相加add,这样得到的效果就是原图中的杂志大部分几乎“消失了”(一次筛选)
6.图像二值化反转，即背景变为黑色，待检测区域为白色
7.进行轮廓检测，记录所有轮廓的变数len
8.细胞核的形状是趋近于椭圆的，它的边数是接近于无穷大，我们给定一个边数下下限，只要边数大于这个下限，我们认为他就是椭圆
9.再对满足边数限制的轮廓的x,y,w,h记录下来，判断其中是否有会交叉的矩形框，并将其排除(二次筛选)
10.将最终的轮廓在原图中画出外接矩形，并记录他们的x,y,w,h，与期望值进行比较，得出查全率，查准率
'''

# series_parametric_tests_by_scanner(xml_path_list, image_path_list)

# test_scanner_method_performance(xml_path_list, image_path_list)